package com.example.dragbtwtworecyclerviews

import android.graphics.Canvas
import android.graphics.Point
import android.os.Build
import android.view.*
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.get
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.*
import kotlin.math.roundToInt

/**
 * 使RecyclerView的item可拖动。
 * 你必须让你的RecyclerViewAdaptor实现这个接口
 * 并创建一个 MyDragListener 实例来让您的 RecyclerView 的项目
 * 可拖动。
 *  @author Burwei
 */
interface BaseDraggableRecyclerviewAdaptor {
    /**
     * 设置RecyclerView的数据。
     * 数据可以是您想要的任何类型，但列表必须是可变的。
     * @param data RecyclerView使用它制作的底层数据
     * 项目列表。
     */
    fun setData(data: MutableList<Any>)

    /**
     * 获取RecyclerView的数据。
     * @return RecyclerView使用它制作的底层数据
     * 项目列表。
     */
    fun getData(): MutableList<Any>

    /**
     * 设置拖动监听器来处理拖动事件。
     * 该方法必须在 setData() 之前调用；否则你会得到
     * 调用此方法时出现 NullPointerException。
     * @param Listener 处理所有拖动事件的拖动监听器。
     */
    fun setDragListener(listener: MyDragListener)

    /**
     * 设置RecyclerView的每一item都是可拖动的。
     * 你必须在onBindViewHolder()中调用这个方法来设置
     * 在每个item的根布局上拖动设置。
     * 所有item都使用相同的拖动侦听器。
     * 这里的“所有item”不仅指这个RecyclerView中的所有item，
     * 还包括您希望其item成为的所有其他 RecyclerView 中的item
     * 可拖动。
     * 并且你必须调用这个方法才能使RecyclerView可拖动，
     * 这样您的item在收到时就不会飞回原来的位置
     * ACTION_DROP拖动事件。 （由于我们使该item不可见，
     * 将物品放置在不可见物品上时，不会收到放置事件，
     * 所以我们需要 RecyclerView 来接收它；否则物品会飞回来
     * 由于拖动事件返回 false 到其原始位置。）
     * @param v 将被设置为可拖动的视图。
     * @param position 该View在父RecyclerView中的位置。
     * @param DragListener 处理所有拖动事件的拖动监听器。
     */
    fun setDrag(v: View, position: Int, dragListener: MyDragListener) {
        var touchedX = 0f  // closure variable
        var touchedY = 0f  // closure variable
        v.visibility = View.VISIBLE
        v.setOnDragListener(dragListener)
        v.setOnTouchListener { v, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    touchedX = event.x
                    touchedY = event.y
                }
            }
            return@setOnTouchListener false  // 将触摸事件留给其他监听者
        }
        v.setOnLongClickListener {
            it.visibility = View.INVISIBLE
            val myShadow = MyDragShadowBuilder(it, touchedX.roundToInt(), touchedY.roundToInt())
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                it.startDragAndDrop(
                        null,
                        myShadow,
                        it,
                        0
                )
            } else {
                it.startDrag(
                        null,
                        myShadow,
                        it,
                        0
                )
            }
        }

    }
}


/**
 * 一个示例 RecyclerViewAdaptor 类来实现 MyDraggableRecyclerviewAdaptor 接口。
 * 该类实现了MyDraggableRecyclerviewAdaptor中的所有方法，
 * 并且还包含一个简单的 OnClickLister 来打印您单击的项目。
 *  @author Burwei
 */
class BaseRecyclerviewAdaptor : RecyclerView.Adapter<BaseRecyclerviewAdaptor.MyViewHolder>(), BaseDraggableRecyclerviewAdaptor
{

    private var clickListener: OnClickListener? = null
    private var myDataset = mutableListOf<Any>()
    private var dragListener: MyDragListener? = null

    interface OnClickListener {
        fun recyclerviewClick(name: String)
    }

    fun setClickListener(parentFragment: OnClickListener) {
        clickListener = parentFragment
    }

    override fun setDragListener(listener: MyDragListener) {
        dragListener = listener
    }

    override fun setData(data: MutableList<Any>) {
        myDataset = data
        notifyDataSetChanged()
    }

    override fun getData(): MutableList<Any> {
        return myDataset
    }


    // 创建新视图（由layout manager调用）
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerviewAdaptor.MyViewHolder {
        // 创建一个新 view
        val item = LayoutInflater.from(parent.context).inflate(R.layout.item_animal, parent, false) as View
        //设置view的大小、边距、填充和布局参数
        return MyViewHolder(item)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val name = myDataset[position]
        holder.txtAnimalName.text = name as String
        holder.layoutAnimal.setOnClickListener {
            clickListener?.recyclerviewClick(name)
        }
        setDrag(holder.layoutAnimal, position,dragListener!!)
    }

    /**
     * 这很重要，给RecyclerView设置一个拖动监听器即可
     * 接收ACTION_DROP拖动事件。
     */
    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        recyclerView.setOnDragListener(dragListener)
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        super.onDetachedFromRecyclerView(recyclerView)
        clickListener = null
        clear()
    }


    // 返回数据集的大小 （由layout manager调用）
    override fun getItemCount() = myDataset.size

    fun clear() {
        myDataset = mutableListOf<Any>()
    }


    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val layoutAnimal: ConstraintLayout = itemView.findViewById(R.id.layoutAnimal)
        val txtAnimalName: TextView = itemView.findViewById(R.id.txtAnimalName)
    }
}


/**
 * MyDragShadowBuilder 是一个简单的 DragShadowBuilder，它创建一个
 * 拖动时带有背景颜色的阴影。
 *  @author Burwei
 */
class MyDragShadowBuilder(v: View, private val touchedX: Int, private val touchedY: Int) : View.DragShadowBuilder(v) {

    /**
     * 设置阴影的大小和位置。
     * 阴影的大小与拖动的视图一样大，并且是初始位置
     * 与拖动视图相同。
     * 该方法从类的属性中获取touchedX和touchedY，并设置
     * 它作为用户触摸这个阴影的点，让阴影初始位置
     * 与拖动的视图相同。
     * @param size 与原始参数相同，请查看官方文档以获取更多信息。
     * @param touch 与原始参数相同，查看官方文档以获取更多信息。
     */
    override fun onProvideShadowMetrics(size: Point, touch: Point) {
        super.onProvideShadowMetrics(size, touch)
        touch.set(touchedX, touchedY)
    }

    /**
     * 设置阴影的背景颜色。
     * 您可以在这里自定义您的阴影。
     * @param canvas 与原始参数相同，请查看官方文档以获取更多信息。
     */
    override fun onDrawShadow(canvas: Canvas) {
        super.onDrawShadow(canvas)
        canvas.drawColor(0x22000000)
    }
}

/**
 * MyDragListener 是处理所有拖动事件的监听器。
 * 它应该与 MyDraggableRecyclerviewAdaptor 一起使用。
 * 所有可拖动的项目都应设置相同的拖动侦听器实例
 * 作为它们的拖动侦听器，以便在实例中使用相同的变量。
 */
class MyDragListener : View.OnDragListener {

    private var isStarted = false
    private var isOriginalParent = true
    private var initPositionInOriParent = 0
    private var initPositionInOtherParent = 0
    private var finalPosition = 0
    private var finalPositionInOriParent = 0
    private var finalParent: RecyclerView? = null


    override fun onDrag(v: View?, event: DragEvent?): Boolean {
        if (v == null || v is RecyclerView || v.parent == null) {
            return true
        }
        when (event?.action) {
            DragEvent.ACTION_DRAG_STARTED -> {
                val sourceView = event.localState as View
                if (!isStarted && sourceView.parent != null) {
                    val sourcePosition = (sourceView.parent as RecyclerView).getChildAdapterPosition(sourceView)
                    initPositionInOriParent = sourcePosition
                    finalPosition = sourcePosition
                    isStarted = true
                }
            }
            DragEvent.ACTION_DRAG_ENTERED -> {
                val sourceView = event.localState as View
                if (sourceView.parent == null) {
                    return true
                }
                val targetAdaptor = (v.parent as RecyclerView).adapter!! as BaseRecyclerviewAdaptor
                val targetPosition = (v.parent as RecyclerView).getChildAdapterPosition(v)
                if (v.parent == sourceView.parent) {
                    if (isOriginalParent) {
                        try {
                            targetAdaptor.notifyItemMoved(finalPosition, targetPosition)
                        } catch (e: Exception) {
                            println("忽略索引越界-")
                        }
                    } else {
                        try {
                            targetAdaptor.notifyItemMoved(finalPositionInOriParent, targetPosition)
                            (finalParent?.adapter as BaseRecyclerviewAdaptor?)?.getData()!!.removeAt(initPositionInOtherParent)
                            finalParent?.adapter?.notifyDataSetChanged()
                        } catch (e: Exception) {
                            println("忽略索引越界--")
                        }
                        isOriginalParent = true
                    }
                    finalPosition = targetPosition
                    finalPositionInOriParent = targetPosition
                } else {
                    if (isOriginalParent) {
                        val sourceValue = ((sourceView.parent as RecyclerView).adapter as BaseRecyclerviewAdaptor).getData()[initPositionInOriParent]
                        try {
                            targetAdaptor.getData().add(targetPosition, sourceValue)
                            targetAdaptor.notifyDataSetChanged()
                            (v.parent as RecyclerView)[targetPosition].visibility = View.INVISIBLE  // not working, don't know why
                            initPositionInOtherParent = targetPosition
                        } catch (e: Exception) {
                            println("忽略索引越界---")
                        }
                        isOriginalParent = false
                        finalPosition = targetPosition
                    } else {
                        if (finalPosition != targetPosition) {
                            try {
                                targetAdaptor.notifyItemMoved(finalPosition, targetPosition)
                            } catch (e: Exception) {
                                println("忽略索引越界----")
                            }
                            finalPosition = targetPosition
                        }
                    }
                }
                finalParent = v.parent as RecyclerView
            }
            DragEvent.ACTION_DRAG_ENDED -> {
                val sourceView = event.localState as View
                if (finalParent == null || sourceView.parent == null) {
                    return true
                }
                val sourceParent = sourceView.parent as RecyclerView
                val sourceValue = ((sourceView.parent as RecyclerView).adapter as BaseRecyclerviewAdaptor).getData()[initPositionInOriParent]
                if (finalParent == sourceView.parent) {
                    (finalParent!!.adapter as BaseRecyclerviewAdaptor).getData().removeAt(initPositionInOriParent)
                    (finalParent!!.adapter as BaseRecyclerviewAdaptor).getData().add(finalPosition, sourceValue)
                } else {
                    (sourceParent.adapter as BaseRecyclerviewAdaptor).getData().removeAt(initPositionInOriParent)
                    (finalParent!!.adapter as BaseRecyclerviewAdaptor).getData().removeAt(initPositionInOtherParent)
                    (finalParent!!.adapter as BaseRecyclerviewAdaptor).getData().add(finalPosition, sourceValue)
                }
                (finalParent!!.adapter as BaseRecyclerviewAdaptor).notifyDataSetChanged()
                (sourceView.parent as RecyclerView?)?.adapter?.notifyDataSetChanged()
                isStarted = false
                finalParent = null
                isOriginalParent = true
            }
        }
        return true
    }
}